package social.hub.impl.jersey.oauth;

import java.util.Map;
import java.util.Map.Entry;

import social.hub.impl.jersey.util.ClientFactory;
import social.hub.oauth.OAuthConfig;
import social.hub.oauth.OAuthProvider;
import social.hub.oauth.OAuthToken;
import social.hub.util.UrlBuilder;
import social.hub.util.Utils;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.representation.Form;
import com.sun.jersey.oauth.client.OAuthClientFilter;
import com.sun.jersey.oauth.signature.OAuthParameters;
import com.sun.jersey.oauth.signature.OAuthSecrets;

public class JerseyOAuthProvider implements OAuthProvider {

	private final Client		client;
	private final OAuthConfig	config;

	public JerseyOAuthProvider(OAuthConfig config) {
		this.config = config;
		client = ClientFactory.createClient();
	}

	public OAuthToken getAccessToken(OAuthToken authorizationToken, Map<String, String> parans) {

		String verifier = parans.get(OAuthParameters.VERIFIER);

		OAuthParameters oauthParameters = createParameters(authorizationToken, verifier);
		OAuthSecrets oauthSecrets = createSecrets(authorizationToken);

		UrlBuilder builder = Utils.createUrlBuilder(config.getAccessTokenUrl(), config);

		for (Entry<String, String> entry : parans.entrySet()) {
			builder.addUrlParan(entry.getKey(), entry.getValue());
		}

		return sendRequest(builder, oauthParameters, oauthSecrets);

	}

	public String getAuthorizationUrl(OAuthToken requestToken) {

		UrlBuilder builder = Utils.createUrlBuilder(config.getAuthorizeUrl(), config);

		if (requestToken != null) {
			builder.addQueryParan("oauth_token", requestToken.getKey());
		}

		return builder.toString();

	}

	public OAuthToken getRequestToken() {

		OAuthToken result = null;

		if (!config.isOAuthVersion2()) {

			OAuthParameters oauthParameters = createParameters();
			OAuthSecrets oauthSecrets = createSecrets();

			UrlBuilder builder = Utils.createUrlBuilder(config.getRequestTokenUrl(), config);

			result = sendRequest(builder, oauthParameters, oauthSecrets);

		}

		return result;

	}

	private OAuthClientFilter createFilter(OAuthParameters oauthParameters, OAuthSecrets oauthSecrets) {
		return new OAuthClientFilter(client.getProviders(), oauthParameters, oauthSecrets, null, null, null, null);
	}

	private OAuthParameters createParameters() {
		return createParameters(null, null);
	}

	private OAuthParameters createParameters(OAuthToken token, String verifier) {

		OAuthParameters result = new OAuthParameters().consumerKey(config.getApiKey());

		if (config.getScope() != null) {
			result.put("scope", config.getScope());
		}

		if (config.getCallbackUrl() != null) {
			result.setCallback(config.getCallbackUrl());
		}

		if (token != null) {
			result.setToken(token.getKey());
		}

		if (verifier != null) {
			result.setVerifier(verifier);
		}

		return result;

	}

	private OAuthSecrets createSecrets() {
		return createSecrets(null);
	}

	private OAuthSecrets createSecrets(OAuthToken token) {

		OAuthSecrets secrets = new OAuthSecrets().consumerSecret(config.getApiSecret());

		if (token != null) {
			secrets.setTokenSecret(token.getSecret());
		}

		return secrets;

	}

	private OAuthToken sendRequest(UrlBuilder builder, OAuthParameters oauthParameters, OAuthSecrets oauthSecrets) {

		OAuthClientFilter oauthFilter = createFilter(oauthParameters, oauthSecrets);

		WebResource resource = client.resource(builder.toString());
		resource.addFilter(oauthFilter);
		Form response = resource.post(Form.class, " ");

		OAuthToken result = null;

		if (!config.isOAuthVersion2()) {

			String token = response.getFirst(OAuthParameters.TOKEN);
			String secret = response.getFirst(OAuthParameters.TOKEN_SECRET);

			if (token != null) {
				result = new OAuthToken(token, secret);
			}

		} else {

			String token = response.getFirst("access_token");

			if (token != null) {
				result = new OAuthToken(token, null);
			}

		}

		return result;

	}

}
